package StudyCode.summary.myfile;

import java.io.*;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

/**
 * 版权所有 科技与人文(www.tah1986.com)
 */
public class CopiesDirectoryPreservingFileDates {
	public static void main(String[] argv) throws Exception {
		copyDirectory(new File("E:\\迅雷下载"), new File("E:\\Copy"));
	}

	private static final long FILE_COPY_BUFFER_SIZE = 1024 * 1024 * 30;

	private static void copyDirectory(File srcDir, File destDir) throws IOException {
		copyDirectory(srcDir, destDir, true);
	}

	private static void copyDirectory(File srcDir, File destDir, boolean preserveFileDate) throws IOException {
		copyDirectory(srcDir, destDir, null, preserveFileDate);
	}

	private static void copyDirectory(File srcDir, File destDir, FileFilter filter, boolean preserveFileDate) throws IOException {
		if (srcDir == null) {
			throw new NullPointerException("源文件夹不能为 null.");
		}
		if (destDir == null) {
			throw new NullPointerException("目标文件夹不能为 null.");
		}
		if (!srcDir.exists()) {
			throw new FileNotFoundException("源文件夹 '" + srcDir
					+ "' 不存在");
		}
		if (!srcDir.isDirectory()) {
			throw new IOException("源文件夹 '" + srcDir + "' 存在，，但不是个目录");
		}
		if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
			throw new IOException("源文件夹 '" + srcDir + "' 跟目标文件夹 '" + destDir + "' 相同");
		}

		List<String> exclusionList = null;
		if (destDir.getCanonicalPath()
				.startsWith(srcDir.getCanonicalPath())) {
			File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir
					.listFiles(filter);
			if (srcFiles != null && srcFiles.length > 0) {
				exclusionList = new ArrayList<String>(srcFiles.length);
				for (File srcFile : srcFiles) {
					File copiedFile = new File(destDir, srcFile.getName());
					exclusionList.add(copiedFile.getCanonicalPath());
				}
			}
		}
		doCopyDirectory(srcDir, destDir, filter, preserveFileDate,
				exclusionList);
	}

	private static void doCopyDirectory(File srcDir, File destDir,
	                                    FileFilter filter, boolean preserveFileDate,
	                                    List<String> exclusionList) throws IOException {
		File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir
				.listFiles(filter);
		if (srcFiles == null) {
			throw new IOException("读取源文件夹 " + srcDir + " 列表失败");
		}
		if (destDir.exists()) {
			if (!destDir.isDirectory()) {
				throw new IOException("目标文件夹 '" + destDir + "' 存在，但不是个目录");
			}
		} else {
			if (!destDir.mkdirs() && !destDir.isDirectory()) {
				throw new IOException("目标文件夹 '" + destDir + "' 不能被创建");
			}
		}
		if (!destDir.canWrite()) {
			throw new IOException("目标文件夹 '" + destDir + "' 不能被写入");
		}
		for (File srcFile : srcFiles) {
			File dstFile = new File(destDir, srcFile.getName());
			if (exclusionList == null
					|| !exclusionList.contains(srcFile.getCanonicalPath())) {
				if (srcFile.isDirectory()) {
					doCopyDirectory(srcFile, dstFile, filter,
							preserveFileDate, exclusionList);
				} else {
					doCopyFile(srcFile, dstFile, preserveFileDate);
				}
			}
		}

		if (preserveFileDate) {
			destDir.setLastModified(srcDir.lastModified());
		}
	}

	private static void doCopyFile(File srcFile, File destFile,
	                               boolean preserveFileDate) throws IOException {
		if (destFile.exists() && destFile.isDirectory()) {
			throw new IOException("目标文件夹 '" + destFile + "' 存在，但不是个目录");
		}

		FileInputStream fis = null;
		FileOutputStream fos = null;
		FileChannel input = null;
		FileChannel output = null;
		try {
			fis = new FileInputStream(srcFile);
			fos = new FileOutputStream(destFile);
			input = fis.getChannel();
			output = fos.getChannel();
			long size = input.size();
			long pos = 0;
			long count = 0;
			while (pos < size) {
				count = (size - pos) > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE
						: (size - pos);
				pos += output.transferFrom(input, pos, count);
			}
		} finally {
			closeQuietly(output);
			closeQuietly(fos);
			closeQuietly(input);
			closeQuietly(fis);
		}

		if (srcFile.length() != destFile.length()) {
			throw new IOException("从 '" + srcFile + "' 拷贝文件到 '" + destFile + "失败'");
		}
		if (preserveFileDate) {
			destFile.setLastModified(srcFile.lastModified());
		}
	}

	private static void closeQuietly(InputStream input) {
		closeQuietly((Closeable) input);
	}

	private static void closeQuietly(OutputStream output) {
		closeQuietly((Closeable) output);
	}

	private static void closeQuietly(Closeable closeable) {
		try {
			if (closeable != null) {
				closeable.close();
			}
		} catch (IOException ioe) {
			// do nothing
		}
	}
}
